<?php
/**
 * 数据库实现类，提供对Mysql的底层操作
 * 
 * @category  Gi
 * @package   Core
 * @author    jufei <jufeis@gmail.com>
 * @version   $Id$
 */
final class Db{

	/*-------私有成员------*/
	private static $_queryConter = 0;		//每页执行query的数目
	
	private $_linkId = 0;		//连接句柄
	private $_queryId = 0;		//查询句柄
	private $_dbConfig;
	private $_pConn = 0;
	private static $_instance = array();
	private static $_lastSql = null;
		
	/**
	 * 构造函数设为私有。 只有用getInstance才能实例化本类
	 */
	private function __construct() {}
	
    /**
     * 取得数据库操作类实例,单件模式
     */
    public static function getInstance($dbConfig = null)
    {
    	$dbConfig = self::_parseConfig($dbConfig);
    	
        $uniqueId = _toUniqueId($dbConfig);
        if (!self::$_instance[$uniqueId]){
        	$className = __CLASS__;
        	$db = new $className();
        	$db->connect($dbConfig);
        	self::$_instance[$uniqueId] = $db;
        }
        return self::$_instance[$uniqueId];
    }
    
    private function _parseConfig($dbConfig){
    	$dbConfig === null && $dbConfig = array();
    	if (is_string($dbConfig)) {
    		$dbConfig = $this->parseDSN($config);
    	}else{
    		$_dbConfig = array (
                'DB_TYPE'        =>   C('DB_TYPE'),
                'DB_USER'  =>   C('DB_USER'),
                'DB_PWD'   =>   C('DB_PWD'),
                'DB_HOST'  =>   C('DB_HOST'),
                'DB_PORT'    =>   C('DB_PORT'),
                'DB_DSN'          =>   C('DB_DSN'),
                'DB_PARAMS'     =>   C('DB_PARAMS'),
            );
            $dbConfig = array_merge($_dbConfig,$dbConfig);
    	}
    	return $dbConfig;
    }
    
    /**
     * 连接数据库
     *
     * @param array $dbConfig
     * @return resource
     */
    public function connect($dbConfig)
    {
    	$dbport = $dbConfig['DB_PORT'] ? $dbConfig['DB_PORT'] : '3306';
    	$dbHost = $dbConfig['DB_HOST'] . ":" . $dbport;
    	$dbName = $dbConfig['DB_NAME'];
    	$dbUser = $dbConfig['DB_USER'];
    	$dbPwd  = $dbConfig['DB_PWD'];
		if($this->_pConn)
			$this->_linkId = @mysql_pconnect($dbHost, $dbUser, $dbPwd) or $this->halt($this->error());
		else
			$this->_linkId = @mysql_connect($dbHost, $dbUser, $dbPwd) or $this->halt($this->error());
		if(!empty($dbName)){
			$this->selectDb($dbName);
		}
		$this->query("SET NAMES UTF8",$this->_linkId);
		return $this->_linkId;
    }

	function selectDb($dbName)
	{
		$this->_queryId = $this->query("use `$dbName`");
		$this->_dbName = $dbName;
		Return $this->_queryId;
	}

	function getCurrentDbName()
	{
		$data = $this->fetch_one_array("select DATABASE() as _dbName");
		$this->_dbName = $data['_dbName'];
		return  $data['_dbName'];
	}
	
	/**
	 * 查询表的主键名
	 * @param string $tbName
	 * @return string
	 */
	function getPkName($tbName){
		$SQL = "describe $tbName";
		$this->query($SQL);
		while( $row=$this->fetchArray()){
			if($row['Key']=='PRI'){
				return $row['Field'];
			}
		} 
		return "";
	}

	/**
	 * 执行一条query
	 * @param string $sql
	 * @param resource $linkId
	 * @return resourceID
	 */
	function query($sql, $linkId = '')
	{
		empty($linkId) && $linkId = $this->_linkId;
		self::$_lastSql = $sql;
		_trace($sql,"SQL");
		$this->_queryId = mysql_query($sql,$linkId) or $this->halt("Query wrong",$this->error());
		self::$_queryConter++;
		Return $this->_queryId;
	}
	
	/**
	 * 顺序读取资源的每一行到数组
	 * @param resource $qid
	 * @param int $result_type
	 * @return array
	 */
	function fetchArray($qid = '',$result_type = MYSQL_ASSOC) 
	{
		if(!$qid)$qid=$this->_queryId;
		Return mysql_fetch_array($qid, $result_type);
	}
	
	/**
	 * 获取query的第一列的值
	 * @param string $sql
	 * @param resource $linkId
	 * @return string|int|float
	 */
	function getVar($sql,$linkId="")
	{
		$data = $this->getRow($sql, $linkId);
		return current($data);
	}
	
	/**
	 * 获取query的第一行的值
	 * @param string $sql
	 * @param resource $linkId
	 * @return array
	 */
	function getRow($sql,$linkId="") 
	{
		$this->query($sql, $linkId);
		return $this->fetchArray();
	}
	
	/**
	 * 获取query的所有行
	 * @param string $sql
	 * @param resource $linkId
	 * @return array
	 */
	function getAllRow($sql,$linkId="")
	{
		$data = array();
		$this->query($sql, $linkId);
		while($row = $this->fetchArray()){
			$data[] = $row;
		}
		return $data;
	}
	
	function error() 
	{
		Return mysql_error();
	}

	function errno() 
	{
		Return intval(mysql_errno());
	}

	function result($row) 
	{
		$this->query_result = @mysql_result($this->_queryId, $row);
		Return $this->query_result;
	}

	function numRows($qid='') 
	{
		if(!$qid)$qid=$this->_queryId;
		$num_rows = mysql_num_rows($qid);
		Return $num_rows;
	}

	function numFields($qid='') 
	{
		if(!$qid)$qid=$this->_queryId;
		Return mysql_num_fields($qid);
	}

	function freeResult($qid='') 
	{
		if(!$qid)$qid = $this->_queryId;
		Return mysql_free_result($this->_queryId);
	}

	function insertId() 
	{
		$this->insert_id = mysql_insert_id();
		Return $this->insert_id;
	}

	function dataSeek($row) 
	{
		Return mysql_data_seek($this->_queryId,$row);
	}
	function close() 
	{
		Return mysql_close();
	}
	function affectedRows() 
	{
		Return mysql_affected_rows($this->_linkId);
	}
	function halt($message = '', $sql = '') 
	{
		_halt("$message<br>$sql");
	}
	/**
	 * 得到一个表的字段列表
	 * @return array
	 * */
	function getTbFields($table)
	{
		$fields = array();
		$this->query( "show fields from $table");	
		while ( $data = $this->fetchArray( '',MYSQL_NUM) ) {
				$fields[] = $data[0];
		}
		return $fields;
	}
    /**
     * 分析数据库配置信息，支持数组和DSN
     * @param string|array $dbConfig 数据库配置信息
     * @return string
     */
    private function parseConfig($dbConfig) 
    {
        if (is_string($dbConfig)) {
            // 如果DSN字符串则进行解析
            $dbConfig = $this->parseDSN($dbConfig);
        }
        return $dbConfig;
    }
    
    /**
     * DSN解析
     * 格式： mysql://username:passwd@localhost:3306/DbName
     * @param string $dsnStr
     * @return array
     */
    public static function parseDSN($dsnStr) 
    {
        if( empty($dsnStr) ){return false;}
        $info = parse_url($dsnStr);
        if($info['scheme']){
            $dsn = array(
            'DB_TYPE'        => $info['scheme'],
            'DB_USER'  => isset($info['user']) ? $info['user'] : '',
            'DB_PWD'   => isset($info['pass']) ? $info['pass'] : '',
            'DB_HOST'  => isset($info['host']) ? $info['host'] : '',
            'DB_PORT'    => isset($info['port']) ? $info['port'] : '',
            'DB_NAME'   => isset($info['path']) ? substr($info['path'],1) : ''
            );
        }else {
            preg_match('/^(.*?)\:\/\/(.*?)\:(.*?)\@(.*?)\:([0-9]{1, 6})\/(.*?)$/',trim($dsnStr),$matches);
            $dsn = array (
            'DB_TYPE'        => $matches[1],
            'DB_USER'  => $matches[2],
            'DB_PWD'   => $matches[3],
            'DB_HOST'  => $matches[4],
            'DB_PORT'    => $matches[5],
            'DB_NAME'   => $matches[6]
            );
        }
        return $dsn;
     }

	/**
	 * 创建一个UTF8字符集的数据库
	 * */
	function createDb($_dbName)
	{
		$this->query("CREATE DATABASE `{$_dbName}` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ");
		
	}
	/**
	 * 导出一个数据库的结构
	 * */
	function getDbStruct($_dbName)
	{
		$schema = "";
		$tables_query = $this->query( "SHOW TABLE STATUS from `" . $_dbName . "`" );
        while ( $tables = $this->fetchArray( $tables_query ) ) {
            if ($tables['Engine']!= null) {

            	$schema .= $this->getTbStruct($_dbName,$tables['Name'], $tables);
            }
		}
		return $schema;
	}
	
	/**
	 * 导出一个表的结构
	 * */
	function getTbStruct($_dbName, $table, $table_info = '')
	{
            $schema = "create table `$table` (\n";
            $table_list = array();
            $fields_query = $this->query( "show fields from `$_dbName`.`$table` ");
			unset($fileds_info);
            while ( $fields = $this->fetchArray( $fields_query ) ) {

				$table_list[] = $fields['Field'];

                $schema .= '  `' . $fields['Field'] . '` ' . $fields['Type'];

                if ( strlen( $fields['Default'] ) > 0 ) $schema .= ' default \'' . $fields['Default'] . '\'';

                if ( $fields['Null'] != 'YES' ) $schema .= ' not null';

                if ( isset( $fields['Extra'] ) ) $schema .= ' ' . $fields['Extra'];

                $schema .= ',' . "\n";
            }

            $schema = ereg_replace( ",\n$", '', $schema );
            // add the keys
            $index = array();
            $keys_query = $this->query( "show keys from `$_dbName`.`$table`");
            while ( $keys = $this->fetchArray( $keys_query ) ) {
                $kname = $keys['Key_name'];

                if ( !isset( $index[$kname] ) ) {$index[$kname] = array( 'unique' => !$keys['Non_unique'],    'columns' => array() );
                }

                $index[$kname]['columns'][] = $keys['Column_name'];
            } while ( list( $kname, $info ) = each( $index ) ) {
                $schema .= ',' . "\n";

                $columns = implode( '`,`',  $info['columns']);

                if ( $kname == 'PRIMARY' ) {$schema .= '  PRIMARY KEY (`' . $columns . '`)';
                } elseif ( $info['unique'] ) {$schema .= '  UNIQUE ' . $kname . ' (`' . $columns . '`)';
                } else {$schema .= '  KEY ' . $kname . ' (`' . $columns . '`)';
                }
            }

            
			$schema .= "\n" . ") ";
			
			if(!empty($table_info['Auto_increment'])){
				$index_id = $table_info['Auto_increment']+10000000;
				$schema .=  " AUTO_INCREMENT = ".$index_id;
				}
			$schema .= ";\n\n";

            return $schema;
	}

	/**
	 * 按照旧的数据库结构生成新的数据库
	 * */
	function copyStructure($db_source,$db_dest){
		$databases = array();
		$this->query("show databases");
		while($data = $this->fetchArray('',MYSQL_NUM)){
			$databases[] = $data[0];
		}
		
		if (!in_array($db_source,$databases)) {
			wrong("要拷贝的数据库 [$db_source] 在数据库中不存在.\nSource database $db_source doesn't exist.");
			return false;
		} else if (in_array($db_dest,$databases)) {
			wrong("要建立的数据库 [$db_dest] 在数据库中已经存在.\nDestination Database $db_dest exists.");
			return false;
		} else {
			$this->createDb($db_dest);
			$this->selectDb($db_dest);
			$sql = $this->getDbStruct($db_source);
			$sql_array = explode(';',$sql);
			foreach ($sql_array as $sql){
				if(trim($sql) !=  ''){
					$this->query($sql);
				}
			}
		}
		return true;
		
	}
}   //end class